ALGOL 68

来自维基百科,自由的百科全书

ALGOL 68(源自英語:ALGOrithmic Language 1968的縮寫),一種指令式程式語言,作為ALGOL家族的成員,是官方上的ALGOL 60後繼者。它的設計目標,是提供更廣泛的應用,以及更嚴格的語法定義。

事实速览 编程范型, 設計者 ...
ALGOL 68
编程范型多范型指令式过程式结构化并发
設計者阿德里安·范·韦恩加登, B.J. Mailloux英语Barry J. Mailloux, J.E.L. Peck英语John E. L. Peck, C.H.A. Koster英语Cornelis H.A. Koster等人
发行时间最终报告: 1968年,​57年前​(1968
修订报告: 1973年,​52年前​(1973
型態系統静态强类型安全结构式英语Structural type system
網站Revised Report on the Algorithmic Language ALGOL 68
主要實作產品
Algol68toC[1], ALGOL 68 Genie[2], ALGOL 68-R英语ALGOL 68-R, ALGOL 68C英语ALGOL 68C, ALGOL 68RS英语ALGOL 68RS, ALGOL 68S英语ALGOL 68S, FLACC英语FLACC
衍生副語言
ALGOL 68r0 (最终报告:1968年),
ALGOL 68r1 (修订报告:1973年)
啟發語言
ALGOL 60, ALGOL X英语ALGOL X, ALGOL Y英语ALGOL Y
影響語言
C[3]C++[4]Bourne shellKornShellBashSteelman英语Steelman language requirementsAdaPython[5]Seed7英语Seed7Mary英语Mary (programming language)S3英语S3 (programming language)
关闭

ALGOL 68的特征包括基于表达式英语Expression-oriented programming language的语法,用户声明的类型结构标签联合类型,变量与引用参数的引用模型可变长数组和字符串、数组与矩阵的分片英语array slicing,用户定义的运算符运算符重载高阶函数匿名函数,以及并发

概論

ALGOL 68由IFIP工作组2.1英语IFIP Working Group 2.1負責設計。1968年12月20日,IFIP工作组2.1通過了這個語法規範,並提交IFIP大會通過且出版。

ALGOL 68的定义使用了阿德里安·范·韦恩加登发明的一种数学形式主义的两级形式文法Van Wijngaarden文法英语Van Wijngaarden grammar使用分别称为“超规则”和“元产生式规则”的两组上下文无关文法规则,生成形成一个可能无限的产生式集合,而这些常规的产生式将识别特定的ALGOL 68程序;值得注意的是,它们能够表达在很多其他编程语言的技术标准中被标记为“语义”的那种要求,其他语言的语义必须用易致歧义的自然语言叙述来表达,并接着在编译器中实现为附加到形式语言解析器的“特设”代码。

ALGOL 68设计的主要目标和原则为:

  1. 描述的完备性和清晰性[6]
  2. 设计的正交性[7]
  3. 安全性[6]
  4. 高效性[6]

在1970年,ALGOL 68-R英语ALGOL 68-R成为了第一个投入工作的ALGOL 68编译器。在1973年9月确定的修订版本中,省略了特定特征比如过程化、gomma和形式边界[6]Stephen R. Bourne是ALGOL 68修订委员会成员,他选取了它的一些想法到他的Bourne shell之中。

ALGOL 68曾受到激烈的批评[8],最突出的是来自其设计委员会的一些成员比如C. A. R. Hoare[9],还有ALGOL 60编译器作者比如Edsger Dijkstra[10],它获评为抛弃了ALGOL 60的简单性,成为了复杂或过于笼统的想法的载体。ALGOL 68与刻意保持简单的同时代(竞争)者如CS-algol英语S-algolPascal形成了鲜明对比。

ALGOL 68的语言定义出版后的文本长达两百多页并充斥着非标准术语,这种复杂性使得编译器实现任务变得困难,故而它曾被称为“没有实现也没有用户”。这么说只是部份真实的,ALGOL 68曾应用于一些小众市场,特别是流行于英国国际计算机有限公司英语International Computers Limited(ICL)的机器之上,还有在教学角色之上。在这些领域之外,其使用相对有限。

尽管如此,ALGOL 68对计算机科学领域的贡献是深刻、广泛而持久的,虽然这些贡献大多只是在它们于后来开发的编程语言中重现之时才被公开认可。很多语言是为了应对设计这门语言时所采用的形式化方法导致的复杂性而专门开发的,其中最著名的是Niklaus WirthALGOL W及其后继者Pascal[11];或者是针对特定角色而重新实现的,比如有些人认为Ada可以看作ALGOL 68的后继者[12]

1970年代的很多语言可以追溯其设计至ALGOL 68,选取一些特征,并放弃被认为太复杂或超出给定角色范围的其他特征。其中就有C语言,它受到ALGOL 68的直接影响,特别是它的强类型结构。多数现代语言都至少可以追溯其部份语法至要么C语言要么Pascal,因而很多语言可直接或间接的经由C语言而追溯至ALGOL 68。

规定和实现时间线

更多信息 名称, 年 ...
名称 国家 描述 目标CPU/平台 属主/许可证 实现语言
广义ALGOL 1962  荷蘭 广义文法的ALGOL[13]
ALGOL 68DR 1968 Does not appear IFIP WG 2.1草案报告[14] Does not appear
ALGOL 68r0 1968 Does not appear IFIP WG 2.1最终报告[15] Does not appear
ALGOL 68-R英语ALGOL 68-R 1970  英国 GEORGE 3英语GEORGE (operating system)之下的ALGOL 68 ICL 1900英语ICT 1900 series 皇家雷达研究所英语Royal Radar Establishment ALGOL 60
DTSS ALGOL 68 1970  美国 Dartmouth分时系统英语Dartmouth Time Sharing System的ALGOL 68[16] GE-635英语GE-600 series 达特茅斯学院
Mini ALGOL 68 1973  荷蘭 针对简单ALGOL 68程序的解释器[17] 可移植解释器 荷兰数学中心 ALGOL 60
OREGANO 1973  美国 实践“实现模型的重要性。”[18] 加州大学洛杉矶分校
M-220 ALGOL 68 1974  苏联 使用EPSILON英语EPSILON (programming language)M-220英语M series (computer)上实现ALGOL 68[19] M-220英语M series (computer) EPSILON英语EPSILON (programming language)
ALGOL 68C英语ALGOL 68C 1975  英国 剑桥大学ALGOL 68实现[20] ICL英语International Computers LimitedIBM System/360PDP-10UnixTelefunken TR440/TR445,Tesla英语Tesla a.s. 200和Z80(1980年)[21] 剑桥大学 ALGOL 68C
ALGOL 68r1 1975 Does not appear IFIP WG 2.1修订报告[22] Does not appear
ALGOL 68 H 1975  荷蘭 加拿大 ALGOL 68编译器[23] 阿尔伯塔大学荷兰数学中心 ALGOL W
CDC ALGOL 68 1975  美国 完全实现的ALGOL 68[24] CDC 6000系列英语CDC 6000 seriesCDC Cyber 控制数据公司
Odra ALGOL 68 1976  苏联 波蘭 Odra英语Odra (computer) 1204/IL ALGOL 60
Oklahoma ALGOL 68 1976  美国 俄克拉何马州立大学ALGOL 68实现[25] IBM 1130System/370 Model 158英语IBM System/370 俄克拉何马州立大学 ANSI Fortran 66
Berlin ALGOL 68 1977  德国 柏林工业大学ALGOL 68实现[26] 基于抽象ALGOL 68机器的机器无关编译器 柏林工业大学 CDL 2英语Compiler Description Language
FLACC英语FLACC 1977  加拿大 具有调试特征的修订报告完整实现 System/370 租用,Chion公司 汇编
ALGOL 68RS英语ALGOL 68RS 1977  英国 可移植编译器 ICL 2900系列英语ICL 2900 SeriesMulticsVMSC生成器(1993年) 皇家信号与雷达研究所英语Royal Signals and Radar Establishment ALGOL 68RS
ALGOL 68-RT英语ALGOL 68-RT 1979  英国 并行ALGOL 68-R
ALGOL 68+ 1980  荷蘭 提议的ALGOL 68的超语言[27]
Leningrad ALGOL 68 1980  苏联 列宁格勒国立大学开发的完全语言加上模块系统 IBM,DEC,CAMCOH,PS 1001和PC
Interactive ALGOL 68英语Interactive ALGOL 68 1983  英国 增量编译,1992年再版MK2[28] PC 非商业共享软件
ALGOL 68S英语ALGOL 68S 1985  英国 美国 ALGOL 68的子集语言[29][30] Sun-3英语Sun-3,Sun SPARC(在SunOS 4.1和Solaris 2下),Atari ST(在GEMDOS下),Acorn Archimedes英语Acorn Archimedes(在RISC OS下),VAX-11英语VAX-11(在Ultrix-32英语Ultrix下) 利物浦大学卡内基·梅隆大学曼彻斯特大学 BLISS英语BLISSPascal
Rutgers ALGOL 68 1990  美国 匈牙利 Mini ALGOL 68解释器后续版本[31] 可移植解释器 写于罗格斯大学的非商业开源软件 C
Algol68toC 1993  英国 基于源自1985年ELLA英语ELLA (programming language)[32] ALGOL 68RS英语ALGOL 68RS的ctrans[33][1] 可移植C生成器 国防研究局英语Defence Research Agency皇冠版权开源软件 ALGOL 68RS
ALGOL 68 Genie 2001  荷蘭 完全语言包括标准的并立子句[2] 可移植解释器;2010年版本2之后,具有可选的选定单元的编译 GNU GPL C
Algol68FrontEnd in GCC 2025  美国 GCC的ALGOL 68前端[34] 跨平台 GNU GPL C/C++
关闭

样例代码

下面是Hello World样例代码,采用了ALGOL 68RS英语ALGOL 68RS的程序结构,严格的说只有在BEGINEND之间的诸行是ALGOL 68程序,第一行、第二行和最后一行都特定于a68toc编译器。

PROGRAM helloworld CONTEXT VOID
USE standard
BEGIN
    print(("hello, world!", newline))
END
FINISH

第一行给出这个程序的标识为helloworld而且这个名字的文件包含了这个程序;CONTEXT VOID短语指定这个程序独立而非嵌入到其他部份之中。第二行的USE加载了标准预置(prelude),print就在其中。将上述代码保存入文本文件helloworld.a68中,然后使用ALGOL 68RS编译器a68toc执行它:

$ ca -u ASDFGHJ helloworld.a68
$ ./helloworld
hello, world!

下面的样例代码实现了埃拉托斯特尼筛法来找到小于等于100的所有素数。ALGOL 68中NIL类似于其他语言中的空指针x OF y表示访问STRUCT y的成员x

BEGIN # ALGOL68的素数筛法,基于链表实现 #
    MODE
        NODE = STRUCT (INT h, REF NODE t),
        LIST = REF NODE;
    OP CONS = (INT n, LIST l) LIST: HEAP NODE := NODE (n, l);
    PRIO CONS = 9;
    PROC one to = (INT n) LIST:
        (PROC fn = (INT m) LIST: (m > n | NIL | m CONS fn(m + 1));
         fn(1));
    PROC error = (STRING s) VOID:
        (print((newline, " error: ", s, newline)); stop);
    PROC hd = (LIST l) INT:
        (l IS NIL | error("hd NIL"); SKIP | h OF l);
    PROC tl = (LIST l) LIST:
        (l IS NIL | error("tl NIL"); SKIP | t OF l);
    PROC show = (LIST l) VOID:
        (l ISNT NIL | print((" ", whole(hd(l), 0))); show(tl(l)) 
         | print(newline));
    PROC filter = (PROC (INT) BOOL p, LIST l) LIST:
    BEGIN
        PROC fn = (LIST m) LIST:
            IF m IS NIL THEN NIL
            ELIF p(hd(m)) THEN hd(m) CONS fn(tl(m))
            ELSE fn(tl(m))
            FI;
        fn(l)
    END;
    PROC sieve = (LIST l) LIST:
        IF l IS NIL THEN NIL
        ELSE hd(l) CONS sieve(filter(
                (INT n) BOOL: n MOD hd(l) NE 0, tl(l)))
        FI;
    PROC primes = (INT n) LIST: sieve(tl(one to(n)));
    show(primes(100))
END

将上述代码保存入文本文件primes.a68中,然后使用ALGOL 68 Genie解释器执行它:

$ a68g primes.a68
 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

显著的语言元素

符号和保留字

标准语言包含六十一个保留字,典型的用粗体字打印,并且其中一些还具有“简短”符号等价者:

MODEOPPRIOPROCFLEXHEAPLOCLONGREFSHORTSTRUCTUNIONVOIDBITSBOOLBYTESCHARCOMPLINTREALSEMASTRINGCHANNELFILEFORMATAT @,IS :=:, ISNT :/=:,OFr0TRUEFALSEEMPTYNIL ○,SKIP ~,
COMMENT  CO # ¢,PRAGMAT  PRBEGIN ~ END ( ~ ),
IF ~ THEN ~ ELIF ~ THEN ~ ELSE ~ FI ( ~ | ~ |: ~ | ~ | ~ ),
CASE ~ IN ~ OUSE ~ IN ~ OUT ~ ESAC ( ~ | ~ |: ~ | ~ | ~ ),
FOR ~ FROM ~ TO ~ BY ~ WHILE ~ DO ~ ODPARGO TO  GOTOEXITr0

GO TO被当作两个保留字,未修订报告的语言还有保留字EITHERIS NOT结合。

符号表示

Thumb
1963年版的ASCII,在后来版本中,被替代为^被替代为_

“臻选字符”(worthy character)是1977年出版的ALGOL 68标准硬件表示报告出于可移植性而推荐的字符集中的字符[35]:

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9   " # $ % ' ( ) * + , - . / : ; < = > @ [ ] _ |

这反映了当年一些硬件不支持在ANSIASCIIIBMEBCDIC之外其他字符的事实。

传输表示采用“臻选字符”时,“加上乘以”符号替代为I,“乘以的幂”符号\替代为E

Thumb
配备APL打字球与键帽后的IBM 2741英语IBM 2741终端的键盘,它不再支持小写字母。

ALGOL的“特殊”字符:

× ÷ ≤ ≥ ≠ ≡ ¬ ∧ ∨ ⊃ ↑ ⏨ ␣ ↓ → ⊥ ○ □ ⌊ ⌈ ⎩ ⎧ ¢

多数都可以在1965年出现的配备APL打字球与键帽后的IBM 2741英语IBM 2741终端的键盘上找到。这些字符也是Unicode标准的一部份,并且在一些流行的字体中可获得到。

单元

基本语言构造是“单元”(unit)。单元可以是“公式”、“封闭子句”、“例程正文”这样的构造,也可以是某个技术上需要的构造诸如赋值、跳转、越过和空无。

技术术语“封闭子句”(enclosed clause)统一了某些固有的加括号的构造,它们在其他当代语言中被称为“”、“do语句”、“switch语句英语Switch statement”等等。在使用关键字的时候,通常使用介入关键字的反转字符序列来终结这个包围,比如:IF ~ THEN ~ ELSE ~ FICASE ~ IN ~ OUT ~ ESACFOR ~ WHILE ~ DO ~ OD。这种守卫命令语法被Stephen Bourne重新使用在常用的Unix Bourne shell之中。

一个表达式还可以产生多元值(multiple value)即有多个元素的一个值,它是通过“并立子句”(collateral clause)从其他一些值构造出来的。这种构造看起来就像过程调用的参数包(pack)。

模态

数据类型在ALGOL 68用语中叫做“模态”(mode),其原始模态的声明符为:INTREALCOMPL复数)、BOOLCHARBITSBYTESSTRING。ALGOL 68不再定义longshortdouble这样的类型,而是提供了修饰符(modifier)LONGSHORT

  • BITSBOOL值的包装向量(packed vector)。
  • BYTESCHAR值的固定大小的紧致多元组(数组)。
  • STRINGCHAR值的FLEX [1 : 0](声明时无元素)的灵活多元组(数组)。
  • LONG – 声明INTREALCOMPL的大小为LONG
  • SHORT – 声明INTREALCOMPL的大小为SHORT

ALGOL 68提供了预置常量(prelude constant)如max reallong max real等来将程序适配到不同实现之中。

复杂模态可以使用模态构造子REFSTRUCTUNIONPROC来创建自更简单的模态:

  • REF mode – 到模态mode的值的引用,类似于C/C++中和Pascal中的指针
  • STRUCT – 用来建造结构,类似于C/C++中的struct和Pascal中的recode
  • UNION – 用来建造联合,类似于C/C++和Pascal中的union
  • PROC – 用来指定过程,类似于C/C++中的函数和Pascal中的过程/函数。

在一个代码块中,诸声明不必须都先于诸语句,但所有名字都必须在使用前声明。ALGOL 68提供了恒等声明来将标识符绑定到常量值之上。在声明给名字的标识符之时,这个名字可以被初始化为立即引用一个值;还可以将名字声明为等同于以前声明的名字而使得二者互为别名。例如:

INT n = 2;  # n是新建的常量,它被固定为2 #
INT m := 3;  # m是新建的局部变量的名字,这个变量的值被初始化为3 #
INT p = m;  # p是新建的常量,它被固定为同于m当前的值 #
INT q := m;  # q是新建的局部变量的名字,这个变量的值被初始化为同于m当前的值 #
REF INT r = m;  # r是新建的变量名字,它与m引用的是同一个变量 #
REAL avogadro = 6.02214076E23;  # 阿伏伽德罗常数 #
LONG REAL long pi = 3.14159 26535 89793 23846 26433 832795;
COMPL imaginary unit = 0 I 1;  # 虚数单位i即复数0 + 1i #

其他声明符号包括:FLEXHEAPLOC

  • FLEX – 所声明的名字是灵活的,就是说它可以引用任何数量元素的多元组(数组)。
  • HEAP – 为变量从全局堆中分配一些空闲空间。
  • LOC – 为变量分配这个局部栈的一些空闲空间。

声明REAL x只是REF REAL x = LOC REAL语法糖LOC REAL是生成REF REAL模态的名字的单元。在REF REAL x = LOC REAL := 0中,赋值LOC REAL := 0导致整数0在被赋值给这个生成器产生的名字之前就被扩充(widen)为实数0.0,声明的名字x与生成器产生的名字二者所引用的是同一个变量。

原始声明符还包括:FORMATFILECHANNELSEMACOMPLEXGPIPEG

  • SEMA – 可以通过运算符LEVEL初始化的信号量

ALGOL 68使用MODE声明来给模态声明名字,它类似于C/C+中的typedef和Pascal中的type

INT max = 99;
MODE NEWMODE = [0:9][0:max]STRUCT (
    LONG REAL a, b, c, SHORT INT i, j, k, REF REAL r);

这类似于如下C99代码:

const int max = 99;
typedef struct {
    double a, b, c; short i, j, k; float *r;
} newmode[9+1][max+1];

对于ALGOL 68,只有模态标示(indicant)NEWMODE出现在等号的左侧,最值得注意的是,构造的制造和读取,是从左至右而不顾及优先级的。还有ALGOL 68多元组(数组)的下界缺省的是1,但也可以是从-max intmax int的任何整数。

模态声明允许递归模态,就是说模态可直接或间接的依据自身来进行定义。这要服从一些限制,例如下列声明是非法的:

MODE A = REF A;
MODE A = STRUCT (A a, B b);
MODE A = PROC (A a) A;

而下列声明是有效的:

MODE A = STRUCT (REF A a, B b);
MODE A = PROC (REF A a) REF A;

注释和编译指导

注释可以按各种方式插入:

¢ 最初方式是在程序中增加两个美分符号 ¢
COMMENT "粗体"注释 COMMENT
CO 风格i注释 CO
# 风格ii注释 #
£ 针对英国键盘的横杠/英镑注释 £

一般而言,注释在ALGOL 68中不能嵌套。这个限制可以使用不同的注释分界符来绕过。

语用指定(pragmat)是在程序中的编译指导,典型的提示给编译器;在新近的语言中叫做“pragma”。例如:

PRAGMAT heap=32 PRAGMAT
PR heap=32 PR

表达式和复合语句

ALGOL 68成为了面向表达式编程语言英语Expression-oriented programming language赋值语句所返回的值是到目的地的引用。因此下列代码在ALGOL 68中是有效的:

REAL half pi, one pi; one pi := 2*(half pi := 2*arc tan(1))

这种表示法也出现在C语言和Perl以及其他一些语言之中。注意在早期语言比如ALGOL 60FORTRAN中,在标识符中的空格是允许的,所以half pi是一个单一的标识符,因而无需采用蛇形命名法驼峰式大小写

另一个例子,要表达数学中f(i)i=1n的总和,下列ALGOL 68整数表达式就可充任:

(INT sum := 0; FOR i TO n DO sum +:= f(i) OD; sum)

注意,作为整数表达式,上述的代码块可以用在“整数值可以使用的任何上下”之中。代码块在ALGOL 68称为“系列子句”(serial clause),它返回其中被求值的最后一个表达式的值;这个概念也出现在LISP以及其他一些语言之上。系列子句加上封闭它的一对圆括号或BEGINEND叫做闭合子句(closed-clause)。

复合语句都终止于独特的闭括号:

IF选择子句

IF 条件 THEN 诸语句 [ ELSE 诸语句 ] FI
简短形式:( 条件 | 诸语句 | 诸语句 )
IF 条件1 THEN 诸语句 ELIF 条件2 THEN 诸语句 [ ELSE 诸语句 ] FI
简短形式:( 条件1 | 诸语句 |: 条件2 | 诸语句 | 诸语句 )

这种方案不仅避免了悬摆else英语dangling else问题,还避免了必须对嵌入的语句序列使用BEGINEND

CASE选择子句

CASE 开关 IN 诸语句, 诸语句, ... [ OUT 诸语句 ] ESAC
简短形式:( 开关 | 诸语句, 诸语句, ... | 诸语句 )
CASE 开关1 IN 诸语句, 诸语句, ... OUSE 开关2 IN 诸语句, 诸语句, ... [ OUT 诸语句 ] ESAC
简短形式:( 开关1 | 诸语句, 诸语句, ... |: 开关2 | 诸语句, 诸语句, ... | 诸语句 )

采用粗体符号的选择子句的例子:

PROC days in month = (INT year, month) INT:
    CASE month IN
    #一月# 31,
    #二月# IF year MOD 4 EQ 0 AND year MOD 100 NE 0 OR year MOD 400 EQ 0 THEN 29 ELSE 28 FI,
          31, 30, 31, 30, 31, 31, 30, 31, 30, 31 # 直到十二月 #
    ESAC;

采用简短符号的选择子句的例子:

PROC days in month = (INT year, month) INT:
    (month|
     31,
     (year%*4 = 0 AND year%*100 /= 0 OR year%*400 = 0 | 29 | 28),
     31, 30, 31, 30, 31, 31, 30, 31, 30, 31);

粗体和简短符号的选择子句可以混合使用。

ALGOL 68允许开关(switch)具有的类型,要么是INT要么是(独有的)UNION。后者允许在UNION变量上施加强类型,参见后面的联合例子。

DO循环子句

[ FOR 索引 ] [ FROM 第一个 ] [ BY 增量 ] [ TO 最后一个 ] [ WHILE 条件  ] DO 诸语句 OD

循环语句的极小化形式是:DO 诸语句 OD

下面是这种“通用”循循的完整例子:

FOR i FROM 1 BY -22 TO -333 WHILE i*i /= 4444 DO ~ OD

这个构造有一些不寻常的方面:

  • 只有DO ~ OD部份是强制性的,在这种情况下循环将无限迭代。
  • 因此子句TO 100 DO ~ OD,将只迭代100次。
  • WHILE“语法元素”允许编程者更早的从FOR循环中破出,例如:
INT sum sq := 0;
FOR i
WHILE
    print(("So far:", i, newline));
    sum sq /= 70**2
DO
    sum sq +:= i**2
OD

后续的对标准ALGOL 68的扩展,允许TO语法元素被替代为UPTODOWNTO来达成小优化。这些编译器还结合了:

  • UNTILC – 用于更晚的循环终止。
  • FOREACHS – 用于并行的运算于多元组(数组)之上。

结构与联合

ALGOL 68支持多字段结构STRUCT标签联合UNIONREF可以前导于任何MODE,包括多元组分片和结构字段。

作为递归模态的例子,下面是传统的链表的声明:

MODE
    VALUE = UNION (VOID, REAL, INT, COMPL, STRING),
    NODE = STRUCT (VALUE val, REF NODE next),
    LIST = REF NODE;

对上述VALUEUNION使用共形子句(conformity clause)的CASE的例子:

VALUE v := "1234";  # 或者 v := EMPTY; #
CASE v IN
    (VOID):     print(("void:", "EMPTY")),
    (REAL r):   print(("real:", r)),
    (INT i):    print(("int:", i)),
    (COMPL c):  print(("compl:", c)),
    (STRING s): print(("string:", s))
    OUT         print("undefined value")
ESAC

未修订报告的语言中共形子句的功能通过在CASE子句中采用共形关系CTAB来实现。

多元组

在ALGOL 68中,不再采用术语数组阵列(array),转而基于数学中的元组-tuple)定义了多元值(multiple value)亦简称多元组(multiple)。多元组由一定数量的元素组成,每个元素都有相同的模态,有时称之为基础模态。多元组的模态由前导着一对方括号的针对每个元素的模态标示组成,它被读作“成行英语Row- and column-major order的模态”(row of mode),它不同于函数式编程语言ML中的对应于代数数据类型积类型英语Product type的“元组”(tuple)。

ALGOL 68多元组的属性之一是它的维度数目。可以声明任何数目维度的多元组,整数的二维多元组拥有模态[,]INT,它被读作“行套行的整数”(row-row-of-int),而实数的三维多元组拥有模态[,,]REAL

MODE VECTOR = [1:3]REAL;  # 向量MODE声明 #
MODE MATRIX = [1:3,1:3]REAL;  # 矩阵MODE声明 #
VECTOR v1 := (1,2,3);  # 向量变量初始化为 (1,2,3) #
[]REAL v2 = (4,5,6);  # 常量元组,类型等价于VECTOR,边界是隐含的 #
OP + = (VECTOR a, b) VECTOR:  # 二元OP即运算符定义 #
    (VECTOR out; FOR i FROM LWB a TO UPB a DO out[i] := a[i]+b[i] OD; out);
MATRIX m := (v1, v2, v1+v2);

这里的冒号分隔构造:第一个元素的下标 : 最后一个元素的下标,被称为裁剪器(trimmer)。

ALGOL 68的分片英语array slicing,在概念上涵盖了向多元组的各个维度加下标英语Subscript and superscript(subscript)从而访问其中特定元素的常规情况,但这个术语更常用的保留给至少一个维度不加下标的情况,比如从矩阵中切分出部份横行(row)或纵列(column)。例如:

REF VECTOR row = m[2,];  # 定义一个REF至矩阵的第二个横行 #
REF VECTOR col = m[,2];  # 定义一个REF至矩阵的第二个纵列 #
print ((m[,2:]));  # 打印矩阵的从第二个至最后一个的纵列 #

省略了裁剪器中第一个下标则假定其为此维度的下界,而省略了第二个下标则将假定其为对应的上界,两个下标都省略则产生下界为1的全部分片。

过程

过程(procedure)的PROC声明对参数和结果值二者要求类型指定,如果没有参数则省略参数列表,如果没有结果值则指定结果模态为VOID

PROC max of real = (REAL a, b) REAL: (a > b | a | b);

过程的返回值是在过程中求值的最后一个的表达式的值。当进行如下过程调用之时:

a := max of real(x, y)

得到等价于进行了如下变换后的结果:

a := REAL (REAL a = x, b = y; (a > b | a | b))

在这个过程之内对变量ab的赋值是非法的。针对需要变更形式参数所提及的值的情况,ALGOL 68提供了传引用调用参数,用来在形式参数列表中指定引用,比如REF REAL。到过程的引用REF PROC也是允许的。下列例子定义一个过程,它将作为参数而指定的一个函数应用于一个数组的每个元素:

PROC apply = (REF []REAL a, PROC (REAL) REAL f) VOID:
    FOR i FROM LWB a TO UPB a DO a[i] := f(a[i]) OD;

这种代码的简单性是在ALGOL 68的前身ALGOL 60中不能达成的。

运算符与关联名字的单元

过程和运算符(operator)合称例程(routine),编程者可以定义新的运算符,并且这些自定义的和预定义的运算符二者都可以重载,并且它们的优先级可以由编码者变更。下列例子定义的运算符MAX具有二元和一元形式二者,一元形式在一个数组的元素之上进行扫描。

PRIO MAX = 9;
OP MAX = (INT a, b) INT: (a > b | a | b);
OP MAX = (REAL a, b) REAL: (a > b | a | b);
OP MAX = (COMPL a, b) COMPL: (ABS a > ABS b | a | b);
OP MAX = ([]REAL a) REAL:
    (REAL out := a[LWB a];
     FOR i FROM LWB a + 1 TO UPB a DO (a[i] > out | out := a[i]) OD;
     out);

初等和二等单元

初等单元(primary)涵盖封闭子句,此类属自身包括:所应用的标识符、调用、铸型、除了例程指示之外的指示和分片。

所应用的标识符(applied-identifier)意味着标识符在一个上下文之中被使用,并非处在其定义之中。

指示(denotation)是其所产生与任何行动都无关的构造,比如实数指示3.14或字符串指示"abc",模态VOID只有一个值,其指示是EMPTY。在其他语言中,指示有时叫做“文字英语Literal (computer programming)”(literal)或“常值”(constant)。

铸型(cast)构成自一个模态标示(indicant)和随后的通常为闭合子句的封闭子句。铸型可被用来提供强位置,在强上下文中能获得所有的强制。例如在REF REAL (xx) := 1中的REF REAL (xx)

调用(call)引起(invoke)一个过程,调用在ALGOL 68 Genie中可以被部份参数化英语Partial application(partial parameterisation):就是说增加实际参数到一个过程的语境(locale)中,当这个语境完全之时调用这个过程,否则发生柯里化

更多信息 优先级, ALGOL68r1“臻选字符” ...
优先级 ALGOL68r1“臻选字符” ALGOL68G
相当于12 加下标[~],分片[~,~],裁剪[~:~]AT @ DIAGTRNSPROWCOL
关闭

二等单元(secondary)包括生成器(generator)和选取(selection)。

更多信息 优先级, ALGOL68r1“臻选字符” ...
优先级 ALGOL68r1“臻选字符” ALGOL68r0−r1 ALGOL68G
相当于11 OFLOCHEAP NEW
关闭

有关的符号在技术上不是运算符,它们转而被当作“关联名字的单元”[36]

三等单元

三等单元(tertiary)包括公式和NIL(可替代为)。公式构成自运算符运算元

在ALGOL 68中对于名字只有一个指示,那就是含义为不引用任何值的NILNIL的模态依赖于上下文,例如:REF INT k = NIL,这里的NIL拥有模态REF INT。尽管NIL是一个名字却不可以对它赋值。

一元运算符
更多信息 优先级, ALGOL68r1“臻选字符” ...
优先级 ALGOL68r1“臻选字符” ALGOL68r1替代符号 ALGOL68C,G ALGOL68r0-r1
相当于10 NOT-UPDOWNLWBUPBABSARGBINENTIERLENGLEVELODDREPRROUNDSHORTEN ¬ ~ NORMTRACETDETINV LWS UPS BTBCTB
关闭
二元运算符及其关联的优先级
更多信息 优先级, ALGOL68r1“臻选字符” ...
优先级 ALGOL68r1“臻选字符” ALGOL68r1替代符号 ALGOL68r0−r1
9 I +* !
8 UP **DOWNLWBUPBSHLSHR ^ ××LWS UPS
7 */OVER %MOD %*ELEM ×÷÷× ÷* ÷:
6 -+
5 LT <LE <=GE >=GT >
4 EQ =NE /= ¬= ~=
3 AND & /\
2 OR \/
1 MINUSAB -:=PLUSAB +:=TIMESAB *:=DIVAB /:=OVERAB %:=MODAB %*:=PLUSTO +=: ×:=÷:=÷×:= ÷*:= %×:= MINUSPLUSTIMESDIVOVERBMODB ÷::=PRUS
关闭

特殊细节:

  • LWSUPS:在ALGOL 68r0中,它们分别在一个多元组(数组)的这个维度的“下界状态”和“上界状态”为固定的(fixed)的情况下返回TRUE
  • LWBUPB:这两个运算符所运算的多元组运算元的模态是ROW,它是指称任何横行英语Row- and column-major order的模态。

四等单元

四等单元(quaternary)包括:赋值、同一关系、例程指示(也称作例程正文)和SKIP(可替代为~)。四等单元并非语言报告中的术语,它是给从单元这个类属中除去封闭子句、初等单元、二等单元和三等单元所余下的只称为“单元”的类属的别名

SKIP产生任何模态的未定义的值,并且只能出现在强上下文中。

更多信息 优先级, ALGOL68r1“臻选字符” ...
优先级 ALGOL68r1“臻选字符” ALGOL68r1替代符号 ALGOL68C,R ALGOL68r0−r1
相当于0 :=IS :=:ISNT :/=: :≠: :¬=: :~=: :=:=C=:=R ..= .=IS NOTCT ::CTAB ::=
关闭

在ALGOL 68中除了恒等声明之外的所有构造都有一个值,它允许链式赋值,例如a := b := c,这些赋值是从右至左执行的。

同一关系包括:IS(可替代为:=:)测试两个引用是否相等;ISNT(可替代为:/=:)测试两个引用是否不相等。同一关系的一侧是可以在强制时解引用来匹配另一侧的强侧,而另一侧则为软侧,不允许两侧都解引用。

强制

强制(coercion)依据三个要件,从被强制者(coercend)产生已强制者(coercee):在应用任何强制之前作为要被强制者的一个先验模态,在这些强制之后作为已被强制者的一个后验模态,已强制者的的语法位置(position)或类属(sort)。强制是可以级联的(cascaded)。

有7种可能的强制,其术语为解过程化(deproceduring),解引用(dereferencing)和弱解引用(weakly-dereferencing),联合化(uniting),扩充(widening)、入行(rowing)和弃置(voiding)。除了联合化之外,每种强制都在所关联的值之上,规制(prescribe)一个相应的动态效果。因此,可以使用强制来编程很多原始行动。

允许特定强制的境况(circumstance)叫做上下文(context)。下面列出每种上下文所固有的强度及其允许的强制:

  • 软(soft)– 解过程化。
  • 弱(weak) – 软强制,随后弱解引用而产生一个名字。
  • 柔(meek)– 软强制,随后解引用。
  • 硬(firm)– 柔强制,随后联合化。
  • 强(strong)– 硬强制,随后扩充、入行或弃置。

在任何上下文中,都有拥有或产生某种模态的值的一个单元,并且在这个上下文中亦有所需的一个模态。如果两个模态不同而现有模态的值能够强制成所需模态的值,则这种强制是合法的。

平衡是将条件、case和共形子句中的交替者(alternative)即可供选择者,和同一关系的两侧,都强制成共同(common)的模态的手段,这使得所涉及构造的上下文会被提升(promote)从而获得这种强制。

强制层级及例子

ALGOL 68有着上下文层级,它确定在程序中特定点之上可获得的强制的种类。这种上下文分为五个层次:

更多信息 INT ...
上下文 上下文位置 可获得的强制 在此上下文中强制例子
  • 恒等(identity)声明的右手侧,如~之于REAL …… = ~
  • 初始化的名字声明的右手侧,如~之于REAL …… := ~
  • 赋值的右手侧,如~之于 …… := ~
  • 调用的实际参数,如~之于 ……(~)
  • 铸型(cast)的封闭子句,如~之于REAL(~)
  • 例程指示的单元
  • 产生VOID的单元
  • 平衡(balanced)子句的除第一部份之外的所有部份
  • 同一关系的一侧,它可能需要解引用来适配于另一侧,
    xx之于xx := x; xx IS x
解过程化 只在弱上下文时软强制再弱解引用 软强制再解引用 柔强制再联合化 硬强制再扩充或入行或弃置

扩充出现在没有精度损失的情况下。例如:

  • LONG INTINT
  • REALINT
  • LONG REALREAL
  • COMPLREAL
  • []BOOLBITS
  • STRINGBYTES

变量可以入行为长度为1的多元组。例如:

  • [1]INTINT
  • [1]REALREAL
  • 公式的运算元,如~之于~ + ~
  • 传输调用的实际参数
例子:

UNION(INT, REAL) var := 1

  • 剪标(trimscript):裁剪器、下标和边界,产生INT
  • 质询(enquiry)子句,如~之于
    IF ~ THEN …… FIFROM ~ BY ~ TO ~ WHILE ~ DO …… OD
  • 调用的初等单元(被调用者),如~之于~(……)
例子:
  • BOOLREF REF BOOL
  • INTREF REF REF INT
  • 分片的初等单元(被分片者),如~之于~[……]
  • 选取的二等单元(被选取者),如~之于…… OF ~
例子:
  • REF INTREF REF INT
  • REF REALREF REF REF REAL
  • REF STRUCTREF REF REF REF STRUCT
  • 赋值的左手侧,如~之于~ := ……
  • 同一关系的另一侧,它被其中一侧可能需要解引用来匹配,
    x之于xx := x; xx IS x
例子:
  • PROC REAL rnd := random的解过程化:rnd
关闭

正交性

ALGOL 68文法的表达依据了一些原始概念:、模态、上下文、强制和短语(phrase)。短语是声明和单元二者之一。共有5种上下文、7种强制、22种不同的单元,和潜在无穷数量的值和模态。在每种上下文中都有可获得的强制。

正交性所称谓的是基本概念可以被没有副作用的组合。原始概念:值、模态、上下文、强制和短语,是相互独立的,它们的组合给予了ALGOL 68少有编程语言拥有的灵活性。举个例子,如果需要模态INT的一个值,比如在多元组的声明的裁剪器或边界中,那么在这个上下文中任何会产生一个整数的单元都可充任。其结果是ALGOL 68程序可以用宽广多样的风格来书写。例如打印从键盘读取的两个数的总和,用常规风格可以写为:

INT a, b;
read((a, b));
print((a + b, newline))

也可以等价的写为:

print(((INT a, b;
    read((a, b));
    a + b), newline))

只要所书写的是合法的ALGOL 68代码,可以采用编程者喜好的任何方式。这种独立性的另一个结果是语言的规则极少有例外。

传输

传输(transput)是ALGOL 68用来称谓输入和输出设施的术语。它包括针对非格式化、格式化和二进制传输的预定义的过程。文件和其他传输设备以机器无关的方式来处理。下面的例子打印出一些非格式化的输出到“标准输出”设备:

print ((newpage, "Title", newline, "Value of i is ",
    i, "and x[i] is ", x[i], newline))

注意预定义的过程newpagenewline作为实际参数而传送。

printread接受可能具有各种所需模态的实际参数,这些例程的形式参数具有成行的联合模态:

PROC print = ([]SIMPLOUT items) VOID: …… ;
MODE SIMPLOUT = UNION (
    INT, REAL, BOOL, CHAR,
    []INT, []REAL, []BOOL, []CHAR,
    [,]INT, [,]REAL, [,]BOOL, [,]CHAR,
    …… );
PROC read = ([]SIMPLIN items) VOID: …… ;
MODE SIMPLIN = UNION (
    REF INT, REF REAL, REF BOOL, REF CHAR,
    REF []INT, REF []REAL, REF []BOOL, REF []CHAR,
    REF [,]INT, REF [,]REAL, REF [,]BOOL, REF [,]CHAR,
    …… );

read使用的模态SIMPLIN是来自各种名字的模态的联合。print使用共形子句来从参数行中每个元素提取出实际的值。

在ALGOL 68中“格式化传输”拥有自己的语法和模式(函数),具有嵌入在两个$字符之间的FORMAT。例如:

printf (($2l"The sum is:"x, g(0)$, m + n)); # 其打印相同于下列: #
print ((new line, new line, "The sum is:", space, whole(m + n, 0))

并行处理

ALGOL 68支持并行处理编程。使用关键字PAR,可以将“并立子句”转换成“并行子句”,这里的行动的同步使用信号量来控制。并行行动在ALOGL 68 Genie中被映射到线程上,如果它们在宿主操作系统上能获得到的话。例如:

PROC
    eat = VOID: (muffins -:= 1; print(("Yum!", newline))),
    speak = VOID: (words -:= 1; print(("Yak...", newline)));
INT muffins := 4, words := 8;
SEMA mouth = LEVEL 1;
PAR BEGIN
    WHILE muffins > 0 DO
        DOWN mouth;
        eat;
        UP mouth
    OD,
    WHILE words > 0 DO
        DOWN mouth;
        speak;
        UP mouth
    OD
END

未修订报告的语言

原本依据1968年最终报告的语言在模态铸型的语法上有所不同,并且它拥有过程化(proceduring)这个特征,就是说,将一个项目的值强制成求值这个项目的一个过程。过程化意图进行惰性求值。最有用的应用是实现布尔运算符的短路求值:

OP ANDF = (BOOL a, PROC BOOL b) BOOL: (a | b | FALSE);
OP ORF = (BOOL a, PROC BOOL b) BOOL: (a | TRUE | b);

ANDF中,b只在aTRUE的情况下才求值。预期下面例子中的print不会被执行:

IF FALSE ANDF (print("Should not be executed"); TRUE) THEN …… FI

语言修订之后,已经不再允许这种从模态BOOL到模态PROC BOOL的强制和铸型。ALGOL 68 Genie将ANDFORF作为扩展而实现为伪运算符。

在语言修订之前,编程者可以通过使用叫做“gomma”的分号替代逗号(comma),从而决定一个过程的参数串行而非并立的求值。例如:

PROC test = (REAL a; REAL b): …… ;
test (x +:= 1, x);

这里保证第一个实际参数求值在第二个实际参数之前,但是在平常情况:

PROC test = (REAL a, b): …… ;
test (x +:= 1, x);

这时编译器可以按它喜好的次序来求值实际参数。

参见

註釋

参考文献

外部链接

Wikiwand - on

Seamless Wikipedia browsing. On steroids.